import { CacheModule, DynamicModule, Logger, Module, OnApplicationShutdown } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { GreeterModule } from '@boticord/greeter'
import ioredisStore from 'cache-manager-ioredis'
import { TypeOrmModule } from '@nestjs/typeorm'
import { AuthModule } from '@boticord/auth'
import { TypeOrmNestLoggerAdapter } from './typeorm-logger.adapter'
import { TypeOrmModelsHost } from './typeorm-models-host.module'
import { ResponseSerializerModule } from '@boticord/response-serializer'
import { AnalyticsModule } from '@boticord/analytics'

/**
 * ABOUT STARTUP MODULE
 *
 * this module is responsible for bootstrapping the application.
 * it is responsible for:
 * - loading the configuration
 * - loading the database
 * - loading the modules
 * - loading the logger
 * - loading the routes
 * - loading the providers
 * - loading the interceptors
 * - loading the filters
 * - loading the shutdown hooks
 *
 * startup module should be injected to any application module.
 * */

@Module({
  imports: [
    ConfigModule.forRoot({
      ignoreEnvFile: true,
    }),
    ResponseSerializerModule.forRoot(),
    CacheModule.registerAsync({
      inject: [ ConfigService ],
      imports: [ ConfigModule ],
      isGlobal: true,
      useFactory: (cfg: ConfigService) => ({
        store: ioredisStore,
        host: cfg.getOrThrow('REDIS_HOST'),
        port: cfg.getOrThrow('REDIS_PORT'),
        password: cfg.get('REDIS_PASSWORD'),
      }),
    }),
    TypeOrmModule.forRootAsync({
      inject: [ ConfigService ],
      imports: [ ConfigModule ],
      useFactory: (cfg: ConfigService) => ({
        type: 'postgres',
        database: cfg.getOrThrow<string>('POSTGRES_DATABASE'),
        port: +cfg.getOrThrow<string>('POSTGRES_PORT'),
        username: cfg.getOrThrow('POSTGRES_USERNAME'),
        password: cfg.getOrThrow('POSTGRES_PASSWORD'),
        host: cfg.getOrThrow('POSTGRES_HOST'),
        autoLoadEntities: true,
        synchronize: true,
        logging: cfg.get('TYPEORM_LOGGING') ?? false,
        logger: new TypeOrmNestLoggerAdapter(new Logger()),
      }),
    }),
    TypeOrmModelsHost,
    AuthModule.forRootAsync({
      inject: [ ConfigService ],
      imports: [ ConfigModule ],
      useFactory: (cfg: ConfigService) => ({
        rpcSecret: cfg.getOrThrow<string>('RPC_SECRET'),
        jwtSecret: cfg.getOrThrow<string>('JWT_SECRET'),
        clientID: cfg.getOrThrow<string>('DISCORD_CLIENT_ID'),
        clientSecret: cfg.getOrThrow<string>('DISCORD_CLIENT_SECRET'),
        scope: cfg.getOrThrow<string>('DISCORD_SCOPES'),
        encryptionSecret: cfg.getOrThrow<string>('AUTH_ENCRYPTION_SECRET'),
        encryptionSalt: cfg.getOrThrow<string>('AUTH_IV_SECRET'),
      }),
    }),
    AnalyticsModule,
  ],
  exports: [ AuthModule, ConfigModule, CacheModule, TypeOrmModule, AnalyticsModule ],
})
export class StartupModule implements OnApplicationShutdown {
  private readonly logger = new Logger(StartupModule.name)

  static forRoot(serviceName: string): DynamicModule {
    return {
      module: StartupModule,
      imports: [ GreeterModule.forRoot(serviceName) ],
    }
  }

  onApplicationShutdown(signal: string) {
    this.logger.log(`Received ${signal}, shutting down...`)
    process.exit(0)
  }
}
